Problemática
Já vimos que as vizinhanças são extremamente importantes para o desenvolvimento de meta-heurísticas de solução única. Mais ainda, quanto maior a vizinhança, maior a chance de encontrarmos o ótimo global.
No entanto, realizar a busca completa por vizinhanças muito grandes pode se tornar inviável computacionalmente
Vamos então entender mais sobre o conceito de tamanho de vizinhança.
Tamanho de vizinhança
Relembrando a definição de vizinhança, visto na aula “5 - Algoritmos de solução única: Conceitos”:
Vizinhança (Neighborhood): Uma função vizinhança \(N\) é um mapeamento \(N:S \rightarrow 2^S\) que atribui a cada solução \(s\in S\) um conjunto de soluções \(N(s) \subset S\)
Em que \(S\) é o conjunto de soluções factíveis para o problema.
Sabemos que as soluções de um problema estão ligadas a uma instância em particular (\(I\)), chamemos então as soluções factíveis de de uma determinada instância como \(S(I)\).
Tamanho de vizinhança
Podemos definir então o tamanho de uma vizinhança \(N(\cdot)\) para uma instância \(I\) como:
Tamanho de vizinhança: \(max\{|N(s)|:s\in S(I)\}\)
Ou seja, considerando uma instancia de um problema, o tamanho da vizinhança é dado pelo maior número de vizinhos que uma solução pode gerar.
Tamanho de vizinhança
Vejamos um exemplo para o problema do TSP com a vizinhança SWAP e 5 cidades:
Podemos fazer o SWAP do ponto um com todos os outros, gerando um total de 4 vizinhos. Da mesma forma, com o ponto 2, gerando mais 3 vizinhos. E com o 3 e 4, gerando mais 3 vizinhos.
Temos então um total de 4 + 3 + 3 = 10 vizinhos. Como todas as soluções factíveis do TSP vão gerar esse mesma quantidade de vizinhos, então o tamanho da vizinhança (para esta instancia) é 10. A fórmula para determinar o tamanho da vizinhança para um problema de 5 pontos é então: \(\frac{5(5-1)}{2} = 10\)
Tamanho de vizinhança
Vamos definir agora o conjunto (possivelmente infinito) de todas as instâncias de um problema de tamanho \(n\) como \(\mathcal{F}(n)\). É possível então definir o tamanho de uma vizinhança como uma função \(f(n)\) do tamanho da instância:
Função tamanho de vizinhança: \(f(n) = max\{|N(s)|:I \in \mathcal{F}(n) ,s\in S(I)\}\)
Podemos então definir o tamanho da vizinhança do SWAP em função do número de pontos, como:
Tamanho de vizinhança
Agora, para determinar a ordem de crescimento de uma função \(f\), usamos a notação “Big-O”. De forma simplificada, essa notação reduz a função ao seu termo de crescimento mais acelerado. Por exemplo, no caso do SWAP, temos:
O termo de maior crescimento é \(\dfrac{n^2}{2}\), portanto dizemos que a ordem de crescimento de \(f(n)\) é:
Ainda, o tamanho da vizinhança SWAP cresce de forma quadrática em função do tamanho da instância \(n\).
Tamanho de vizinhança
Com essa terminologia em mente, Ahuja et. al define o conceito de Very-Large Neighborhoods como vizinhanças que crescem de forma exponencial em função do tamanho das instâncias \(n\), e propõe uma classificação de algoritmos que resolvem problemas com essas vizinhanças.
1
Nós estudaremos 2 deles:
- LNS - Large Neighborhood Search
- ALNS - Adaptive Large Neighborhood Search
1Para um estudo de vizinhanças exponenciais para o TSP veja 2000 - A study of exponential neighborhoods for the Travelling Salesman Problem and for the Quadratic Assignment Problem
LNS - A idéia
A meta-heurística Large Neighborhood Search foi proposta por Shaw (1998 - Using Constraint Programming and Local Search Methods to Solve Vehicle Routing Problems - Shaw)
O LNS usa vizinhanças de forma implicita, por meio de operadores de destruição e de reconstrução
- DESTRUIÇÃO: Destrói parte da solução, usualmente de forma estocástica.
- RECONSTRUÇÃO: Reconstrói a solução destruída, retornando-a para a factibilidade.
A vizinhança \(N(s)\) de uma solução é definida então pelas soluções que podem ser alcançadas pela aplicação do operador de destruição e depois de reconstrução.
LNS - Exemplo VRP
Considere uma solução do VRP.
LNS - Exemplo VRP
Um operador de destruição poderia simplesmente selecionar 15% dos clientes de forma aleatória, e removê-los de suas rotas.
LNS - Exemplo
Em seguida um operador de reconstrução poderia reinseri-los novamente, usando alguma heurística gulosa, por exemplo.
LNS - Pseudocódigo
LNS - Considerações
Algumas considerações:
- No método original, a função “aceita” só aceita soluções melhores, artigos posteriores usaram critérios como o SA.
- É importante implementar uma taxa de destruição para o operador de destruição: se somente uma pequena parte da solução é destruída, a busca pode ficar muito limitada, se muito for destruída, é como um reinicio aleatório.
- Shaw propõe um aumento gradativo da taxa de destruição, já Ropke and Pisinger selecionam de forma aleatória, para cada iteração, dentro de um range definido de acordo com o tamanho da instância.
ALNS - Diferença
A meta-heurística Adaptive Large Neighborhood Search (ALNS), proposta em 2006 - An adaptive large neighborhood search heuristic for the pickup and delivery problem with time windows - Ropke, Pisinger, é uma generalização da LNS, permitindo múltiplos operadores de destruição e reconstrução.
A escolha de qual operador usar é controlada dinamicamente durante a busca (de forma adaptativa - daí o nome), usando a performance de cada operador em iterações passadas. O mecanismo de adaptação tende a usar mais os operadores que mais contribuem para a melhoria das soluções
Notação
Considere a seguinte notação:
- \(\Omega^-\): Conjunto de todos os operadores de destruição.
- \(\Omega^+\): Conjunto de todos os operadores de reconstrução.
- \(\rho^-\): Conjunto com os pesos para cada operador de destruição.
- \(\rho^+\): Conjunto com os pesos para cada operador de construção.
Vejamos o algoritmo completo, junto ao LNS previamente visto. As partes em vermelho são as contribuições do ALNS, todo o resto é exatamente o mesmo LNS já visto.
ALNS - Pseudocódigo
Mecanismo adaptativo
Agora precisamos entender como o mecanismo de escolha de operadores é auto regulado. Inicialmente os pesos \(\rho^+\) e \(\rho^-\) são iguais, de forma que todas as vizinhanças possuem as mesmas probabilidades de serem selecionadas. A escolha é feita pelo método da roleta, por exemplo, considere \(\rho^+\) para 5 vizinhanças:
As probabilidades de escolha ficam:
Para usar a roleta basta construirmos um vetor de probabilidades acumuladas:
E selecionarmos um número aleatório entre 0 e 1, determinando o operador da faixa selecionada
Mecanismo adaptativo
Os pesos \(\rho\) são ajustados dinamicamente baseado no desempenho armazenado de cada operador de destruição e reconstrução. Ao fim de cada iteração, um score \(\psi\) é calculado pela fórmula:
\( \psi = max \left\{{\def\arraystretch{1.242}\kern-0.4em\begin{array}{ll} \omega_1: \textsf{se a nova solução é a melhor global} \\ \omega_2: \textsf{se a nova solução é melhor que a atual} \\ \omega_3: \textsf{se a nova solução é aceita} \\ \omega_4: \textsf{se a nova solução é rejeitada} \\ \end{array}\kern-0.5em}\right. \)
Em que \(\omega_1,\omega_2,\omega_3,\omega_4\) são parâmetros, geralmente com \(\omega_1 \geq \omega_2 \geq \omega_3 \geq\omega_4\). Ou seja, um alto valor de \(\mathbb{\psi}\) indica que a iteração (portanto operador de destruição e reconstrução) foi um sucesso.
Mecanismo adaptativo
Agora, com o valor de \(\psi\) os pesos \(\rho^+\) e \(\rho^-\) podem ser atualizados. Considerando \(a\) e \(b\) os índices dos operadores de destruição e reconstrução usados na última iteração, temos que:
Com \(\alpha \in [0,1]\). O parâmetro \(\alpha\) é chamado de decaimento (decay), e controla o quão sensível os pesos são às mudanças na performance dos operdores. Altos valores de \(\alpha\) são mais conservadores, mantendo a maior parte dos novos pesos como estavam antes da iteração. Já valores baixos favorecem mais alterações.
Mecanismo adaptativo
Vejamos um exemplo. Suponha que estamos na primeira iteração, portanto :
Considerando ainda que neste iteração usamos a vizinhança de destruição 0 (\({\color{#b6321d}a = 0}\)) e de reconstrução 2 (\({\color{#b6321d}b = 2}\).)
Com os seguintes valores de \(\omega\)
\( \psi = max \left\{{\def\arraystretch{1.242}\kern-0.4em\begin{array}{ll} \omega_1 = 8: \textsf{se a nova solução é a melhor global} \\ \omega_2 = 4: \textsf{se a nova solução é melhor que a atual} \\ \omega_3 = 2: \textsf{se a nova solução é aceita} \\ \omega_4 = 1: \textsf{se a nova solução é rejeitada} \\ \end{array}\kern-0.5em}\right. \)
Com os seguintes valores de \(\omega\)
\( \psi = max \left\{{\def\arraystretch{1.242}\kern-0.4em\begin{array}{ll} \omega_1 = 8: \textsf{se a nova solução é a melhor global} \\ {\color{#0061a5}\omega_2 = 4: \textsf{se a nova solução é melhor que a atual}} \\ \omega_3 = 2: \textsf{se a nova solução é aceita} \\ \omega_4 = 1: \textsf{se a nova solução é rejeitada} \\ \end{array}\kern-0.5em}\right. \)
E finalmente, após essa iteração a nova solução ficou melhor que a atual.
Mecanismo adaptativo
Assim, precisamos usar as fórmulas:
Com \(\psi = 4\), \(a = 0\) e \(b = 2\). Só faltando determinar o coeficiente de sensibilidade \(\alpha\). Vamos calcular com 2 valores de \(\alpha\) e ver o que ocorre (0.2 e 0.8):
Mecanismo adaptativo
Dessa forma, os novos vetores de pesos (para os dois valores de \(\alpha\)) ficam:
O que confirma que o \(\alpha\) determina a sensibilidade dos pesos a mudanças - um \(\alpha\) menor gera pesos mais sensíveis, e um \(\alpha\) maior gera menos sensibilidade.
Conclusões
- As meta-heurísticas LNS e ALNS são usadas quando as vizinhanças para um problema são muito grandes, de forma que os algoritmos trabalham com vizinhanças implícitas, por meio dos operadores de destruição e reconstrução.
- Também podem ser usadas em problemas que a definição de vizinhanças não é tão clara, ou mesmo não é possível.
- Se a solução inicial foi criada por um algoritmo guloso, podemos aproveitar a própria heurística de escolha dos elementos gulosos como um operador de reconstrução. Assim, somente determinando um operador de destruição já temos uma meta-heurística muito poderosa de forma simples a partir do greedy.
- Devido a alta flexibilidade da escolha de operadores no ALNS, esta meta-heurística se encaixa muito bem para ser usada em mathheuristics (por exemplo, destruindo uma parte da solução e reconstruindo-a usando um modelo matemático com métodos exatos)
Atividade
- Considerando o artigo 2000 - A study of exponential neighborhoods for the Travelling Salesman Problem and for the Quadratic Assignment Problem, entenda a vizinhança ASSIGN para o TSP e crie um exemplo numérico explicando como a mesma funciona, e a sua relação com o problema de designação.
- Pisisnger e Ropke usaram ALNS como um algoritmo que resolve diversas variantes do problema VRP de uma só vez, em 2007 - A general heurisitc for vehicle routing problems - Ropke, Pisinger. No artigo eles também fazem uma explicação da ALNS. Leia o artigo e as partes destacadas, relacionando com a aula. Identifique os operadores de destruição e reconstrução utilizados.